#include <mex.h>
#include "gmatlab.h"

/*
 * g_matlab_error:
 * @id: (allow-none): matlab message identifier
 * @format: printf-style format string for error message
 *
 * Throws a matlab exception with given error message.
 * Executes g_matlab_cleanup() before throwing the error (but after
 * constructing the error message from the arguments).  The @id is
 * duplicated, allowing it to be freed at cleanup, if necessary.
 * Note: This function does not return.
 */
void g_matlab_error(const gchar *id, const gchar *format, ...)
{
  // Perhaps consider using a static buffer instead?
  GString *msg = g_string_new(NULL);
  gchar *str, *ID = (id ? g_strdup(id) : NULL);
  va_list args;
  va_start(args, format);
  g_string_vprintf(msg, format, args);
  va_end(args);
  str = msg->str;
  g_string_free(msg, FALSE);
  g_matlab_cleanup();
  g_matlab_cleanup_add(g_free, str);
  if (ID)
  {
    g_matlab_cleanup_add(g_free, ID);
    mexErrMsgIdAndTxt(ID, str);  // Does not return
  } else mexErrMsgTxt(str);  // Does not return
}

/*
 * g_matlab_error_with_error:
 * @id: (allow-none): matlab message identifier
 * @err: (allow-none): a #GError (or %NULL)
 * @format: printf-style format string for error message
 *
 * Throws a matlab exception with given error message.  If @err is provided,
 * the information therein is formatted along with the message.
 * Executes g_matlab_cleanup() before throwing the error (but after
 * constructing the error message from the arguments).  The @id is
 * duplicated, allowing it to be freed at cleanup, if necessary.
 * Note: This function does not return.
 */
void g_matlab_error_with_error(const gchar *id, GError *err,
                               const gchar *format, ...)
{
  GString *msg = g_string_new(NULL);
  gchar *str, *ID = (id ? g_strdup(id) : NULL);
  va_list args;
  va_start(args, format);
  g_string_vprintf(msg, format, args);
  va_end(args);
  if (err != NULL)
  {
    g_string_append_printf(msg, "\n%s(%d), %s", g_quark_to_string(err->domain),
                           err->code, err->message);
    g_error_free(err);
  }
  str = msg->str;
  g_string_free(msg, FALSE);
  g_matlab_cleanup();
  g_matlab_cleanup_add(g_free, str);
  if (ID)
  {
    g_matlab_cleanup_add(g_free, ID);
    mexErrMsgIdAndTxt(ID, str);  // Does not return
  } else mexErrMsgTxt(str);  // Does not return
}

/*
 * g_matlab_log_func:
 * @log_domain: the log domain of the message
 * @log_level: the log level of the message
 * @message: the message to process
 * @data: unused
 *
 * Transfers log messages from glib to matlab. Warnings and critical messages
 * are transferred as matlab warnings.  Never called directly.
 */
void g_matlab_log_func(const gchar *log_domain, GLogLevelFlags log_level,
                       const gchar *message, gpointer user_data)
{
  GString *str = g_string_new(log_domain ? log_domain : "gmatlab");
  gchar *id;
  int i;
  
  // Cannonicalize domain
  for (i=0; i < str->len; i++)
  {
    gchar c = str->str[i];
    if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
          (c >= '0' && c <= '9') || (c == '_')))
      str->str[i] = '_';
  }
  if (str->str[0] == '_' || (str->str[0] >= '0' && str->str[0] <= '9'))
    g_string_prepend_c(str, 'X');
  
  switch (log_level & G_LOG_LEVEL_MASK)
  {
    case G_LOG_LEVEL_ERROR:    g_string_append(str, ":error");     break;
    case G_LOG_LEVEL_CRITICAL: g_string_append(str, ":critical");  break;
    case G_LOG_LEVEL_WARNING:  g_string_append(str, ":warning");   break;
    case G_LOG_LEVEL_MESSAGE:  g_string_append(str, ":message");   break;
    case G_LOG_LEVEL_INFO:     g_string_append(str, ":info");      break;
    case G_LOG_LEVEL_DEBUG:    g_string_append(str, ":debug");     break;
    default:                   g_string_append(str, ":log");       break;
  }
  id = str->str;
  /* Note: We can't use mexErrMsgIdAndTxt(), as this function does not return
     to allow the g_log() cycle to complete.  At the next g_log(), whatever
     the level, glib would abort() due to recursion. */
  if (log_level & (G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_ERROR |
                   G_LOG_LEVEL_WARNING))
    mexWarnMsgIdAndTxt(id, message);
  else
    mexPrintf("%s: %s\n", id, message);
  // TODO: Allow a quiet setting to surpress the informational messages
  g_string_free(str, TRUE);
}
